using CppAst;

namespace ast;

public class ClassUtil
{
    public static string FirstToUpper(string name)
    {
        var c = name[0].ToString().ToUpper();
        return string.Concat(c, name.AsSpan(1));
    }

    public static CppType GetLowestType(CppType cppType)
    {
        if (cppType is CppTypeWithElementType cppTypeWithElementType)
            return GetLowestType(cppTypeWithElementType.ElementType);

        return cppType;
    }


    public static bool FunctionCanNotExport(ModuleInfo moduleInfo, CppFunction function, ClassInfo? classInfo)
    {
        if (function.Name == "createRigidDynamic")
        {
        }

        if (function.IsFunctionTemplate)
        {
            return true;
        }

        if (function.Visibility == CppVisibility.Private) return true;
        if (function.Visibility == CppVisibility.Protected) return true;
        var cppFunctionFlags = function.Flags;
        if ((cppFunctionFlags & CppFunctionFlags.FunctionTemplate) > 0) return true;
        if ((cppFunctionFlags & CppFunctionFlags.OverloadedOperator) > 0) return true;
        if ((cppFunctionFlags & CppFunctionFlags.MoveConstructor) > 0) return true;
        if ((cppFunctionFlags & CppFunctionFlags.CopyConstructor) > 0) return true;
        if ((cppFunctionFlags & CppFunctionFlags.Deleted) > 0) return true;

        if (classInfo != null)
        {
            if (function.IsConstructor || function.IsNewArray)
            {
                if ((cppFunctionFlags & CppFunctionFlags.Virtual) > 0)
                {
                    return true;
                }


                //operator new 没有默认的
                if (!HasDefaultNewOperator(classInfo.ClassType, out var _))
                {
                    return true;
                }

                if (!HasAvailableDestructor(moduleInfo, classInfo, classInfo.ClassType))
                {
                    return true;
                }

                if (function.IsDefaultConstructor)
                {
                    //默认的构造函数只能是无参构造函数
                    if (!HasPublicNoArgumentConstructor(classInfo.ClassType))
                    {
                        return true;
                    }
                }

                if (function.IsNewArray)
                {
                    //没有默认的new []
                    if (!HasDefaultNewArrayOperator(classInfo.ClassType, out var _))
                    {
                        return true;
                    }

                    //默认的构造函数只能是无参构造函数
                    if (!HasPublicNoArgumentConstructor(classInfo.ClassType))
                    {
                        return true;
                    }
                }
            }


            if (function.IsDestructor || function.IsDeleteArray)
            {
                //没有默认delete operator
                if (!HasDefaultDeleteOperator(classInfo.ClassType, out var _))
                {
                    return true;
                }

                if (function.IsDeleteArray)
                {
                    if (!HasAvailableDestructor(moduleInfo, classInfo, classInfo.ClassType))
                    {
                        return true;
                    }

                    //没有默认的delete []
                    if (!HasDefaultDeleteArrayOperator(classInfo.ClassType, out var _))
                    {
                        return true;
                    }
                }
            }
        }


        foreach (var functionParameter in function.Parameters)
        {
            var lowestType = GetLowestType(functionParameter.Type);
            if (!TypeCanExport(moduleInfo, lowestType))
            {
                return true;
            }
        }

        var cppType = GetLowestType(function.ReturnType);
        if (!TypeCanExport(moduleInfo, cppType))
        {
            return true;
        }

        if (function.ReturnType is CppClass returnClass && !function.IsConstructor)
        {
            if (!IsAbstract(returnClass) && !HasPublicNoArgumentConstructor(returnClass))
            {
                return true;
            }
        }

        return false;
    }

    private static bool HasAvailableDestructor(ModuleInfo moduleInfo, ClassInfo classInfo, CppClass classInfoClassType)
    {
        var failedCnt = 0;
        var methodCnt = 0;
        foreach (var cppFunction in classInfoClassType.Destructors)
        {
            methodCnt++;
            if (FunctionCanNotExport(moduleInfo, cppFunction, classInfo))
            {
                failedCnt++;
            }
        }

        if (methodCnt == 0)
        {
            return true;
        }

        return failedCnt < methodCnt;
    }

    public static bool HasPublicNoArgumentConstructor(CppClass cppClass)
    {
        foreach (var constructor in cppClass.Constructors)
        {
            var needSetParaCnt = 0;
            foreach (var cppParameter in constructor.Parameters)
            {
                if (cppParameter.InitExpression == null && cppParameter.InitValue == null)
                {
                    needSetParaCnt++;
                }
            }

            if (needSetParaCnt == 0)
            {
                if (constructor.Visibility == CppVisibility.Public)
                {
                    return true;
                }
            }
        }

        return false;
    }


    public static bool HasDefaultNewOperator(CppClass cppClass, out bool hasDefine)
    {
        hasDefine = false;
        foreach (var function in cppClass.Functions)
        {
            if (function.Name == "operator new")
            {
                hasDefine = true;
                if (function.Parameters.Count == 1 && function.Visibility != CppVisibility.Private &&
                    function.Visibility != CppVisibility.Protected)
                {
                    return true;
                }
            }
        }

        foreach (var baseType in cppClass.BaseTypes)
        {
            if (baseType.Type is not CppClass parentClass)
            {
                continue;
            }

            if (HasDefaultNewOperator(parentClass, out var parentHasDefine))
            {
                return true;
            }

            if (parentHasDefine)
            {
                hasDefine = true;
            }
        }

        if (!hasDefine)
        {
            return true;
        }

        return false;
    }

    public static bool HasDefaultNewArrayOperator(CppClass cppClass, out bool hasDefine)
    {
        hasDefine = false;
        foreach (var function in cppClass.Functions)
        {
            if (function.Name == "operator new[]")
            {
                hasDefine = true;
                if (function.Visibility != CppVisibility.Private && function.Visibility != CppVisibility.Protected)
                {
                    if (function.Parameters.Count == 1)
                    {
                        return true;
                    }
                }
            }
        }

        foreach (var baseType in cppClass.BaseTypes)
        {
            if (baseType.Type is not CppClass parentClass)
            {
                continue;
            }

            if (HasDefaultNewArrayOperator(parentClass, out var parentHasDefine))
            {
                return true;
            }

            if (parentHasDefine)
            {
                hasDefine = true;
            }
        }

        if (!hasDefine)
        {
            return true;
        }

        return false;
    }

    public static bool HasDefaultDeleteOperator(CppClass cppClass, out bool hasDefine)
    {
        hasDefine = false;
        foreach (var function in cppClass.Functions)
        {
            if (function.Name == "operator delete")
            {
                hasDefine = true;
                if (function.Visibility != CppVisibility.Private &&
                    function.Visibility != CppVisibility.Protected)
                {
                    if (function.Parameters.Count == 1)
                    {
                        return true;
                    }
                }
            }
        }

        foreach (var baseType in cppClass.BaseTypes)
        {
            if (baseType.Type is not CppClass parentClass)
            {
                continue;
            }

            if (HasDefaultDeleteOperator(parentClass, out var parentHasDefine))
            {
                return true;
            }

            if (parentHasDefine)
            {
                hasDefine = true;
            }
        }

        if (!hasDefine)
        {
            return true;
        }

        return false;
    }

    public static bool HasDefaultDeleteArrayOperator(CppClass cppClass, out bool hasDefine)
    {
        hasDefine = false;
        foreach (var function in cppClass.Functions)
        {
            if (function.Name == "operator delete[]")
            {
                hasDefine = true;
                if (function.Visibility != CppVisibility.Private && function.Visibility != CppVisibility.Protected)
                {
                    if (function.Parameters.Count == 1)
                    {
                        return true;
                    }
                }
            }
        }

        foreach (var baseType in cppClass.BaseTypes)
        {
            if (baseType.Type is not CppClass parentClass)
            {
                continue;
            }

            if (HasDefaultDeleteArrayOperator(parentClass, out var parentHasDefine))
            {
                return true;
            }

            if (parentHasDefine)
            {
                hasDefine = true;
            }
        }

        if (!hasDefine)
        {
            return true;
        }

        return false;
    }

    public static bool TypeCanExport(ModuleInfo moduleInfo, CppType cppType)
    {
        if (cppType is CppFunctionType)
        {
            return false;
        }

        if (cppType is CppUnexposedType)
        {
            return false;
        }

        if (cppType is CppClass cppClass)
        {
            if (cppClass.Parent != null && cppClass.Parent is CppClass parentClass)
            {
                if (!TypeCanExport(moduleInfo, parentClass))
                {
                    return false;
                }
            }

            if (cppClass.TemplateKind == CppTemplateKind.TemplateClass ||
                cppClass.TemplateKind == CppTemplateKind.PartialTemplateClass)
            {
                return false;
            }


            if (cppClass.TemplateKind == CppTemplateKind.TemplateSpecializedClass)
            {
                foreach (var cppClassTemplateSpecializedArgument in cppClass.TemplateSpecializedArguments)
                {
                    if (cppClassTemplateSpecializedArgument.ArgAsType != null)
                    {
                        var argAsType = SimpleType(cppClassTemplateSpecializedArgument.ArgAsType);

                        if (argAsType is CppClass)
                        {
                            return false;
                        }

                        var lowtype = GetLowestType(argAsType);
                        if (lowtype is CppClass && argAsType is CppReferenceType)
                        {
                            return false;
                        }

                        if (!TypeCanExport(moduleInfo, lowtype))
                        {
                            return false;
                        }
                    }
                    else
                    {
                        continue;
                    }
                }
            }

            if (cppType.SourceFile == null)
            {
                return false;
            }

            if (!cppType.SourceFile.StartsWith(moduleInfo.ModuleDir))
            {
                return false;
            }


            if (cppClass.Visibility is CppVisibility.Private or CppVisibility.Protected)
            {
                return false;
            }


            var baseTypeValid = true;
            foreach (var cppClassBaseType in cppClass.BaseTypes)
            {
                if (cppClassBaseType.Type is CppUnexposedType)
                {
                    continue;
                }

                if (!TypeCanExport(moduleInfo, cppClassBaseType.Type))
                {
                    baseTypeValid = false;
                    break;
                }
            }

            if (!baseTypeValid) return false;

            if (cppClass.ClassKind == CppClassKind.Union) return false;

            if (!moduleInfo.cppClassMap.ContainsKey(cppType.FullName))
            {
                return false;
            }

            return true;
        }

        return true;
    }

    public static bool IsVoidAnyPtr(CppType cppType)
    {
        var simpleType = SimpleType(cppType.Clone());
        if (simpleType is CppPointerType cppPointerType &&
            cppPointerType.ElementType is CppPrimitiveType cppPrimitiveType &&
            cppPrimitiveType.Kind == CppPrimitiveKind.Void)
        {
            return true;
        }

        return false;
    }

    public static CppType SimpleType(CppType cppType)
    {
        cppType = CleanTypeDefType(cppType.Clone());
        if (cppType is not CppTypeWithElementType) return cppType;

        var cppTypeWithElementType = cppType as CppTypeWithElementType;
        if (cppTypeWithElementType is CppQualifiedType cppTypedef)
        {
            var childHead = SimpleType(cppTypedef.ElementType.Clone());
            return childHead;
        }

        cppTypeWithElementType!.ElementType = SimpleType(cppTypeWithElementType.ElementType.Clone());
        return cppTypeWithElementType;
    }

    public static CppType CleanTypeDefType(CppType inType)
    {
        var cppType = inType.Clone();
        if (cppType is not CppTypeWithElementType) return cppType;

        var cppTypeWithElementType = cppType as CppTypeWithElementType;
        if (cppTypeWithElementType is CppTypedef cppTypedef)
        {
            var childHead = CleanTypeDefType(cppTypedef.ElementType.Clone());
            return childHead;
        }

        cppTypeWithElementType!.ElementType = CleanTypeDefType(cppTypeWithElementType.ElementType.Clone());
        return cppTypeWithElementType;
    }

    public static bool IsAbstract(CppClass cppClass)
    {
        if (cppClass.IsAbstract)
        {
            return true;
        }

        foreach (var cppClassFunction in cppClass.Functions)
        {
            if (cppClassFunction.IsPureVirtual)
            {
                return true;
            }
        }

        return false;
    }

    public static string TypeToString(CppType cppType, string name)
    {
        if (string.IsNullOrEmpty(name))
        {
            return cppType.FullName;
        }

        if (cppType is CppPointerType cppPointerType && cppPointerType.ElementType is CppArrayType cppArrayType)
        {
            var lowType = TypeUtil.GetArrayElementType(cppArrayType);
            return $"{lowType.GetDisplayName()}(*{name}){TypeUtil.GetArrayDimension(cppArrayType)}";
        }
        else if (cppType is CppReferenceType cppReferenceType &&
                 cppReferenceType.ElementType is CppArrayType cppArrayType2)
        {
            var lowType = TypeUtil.GetArrayElementType(cppArrayType2);
            return $"{lowType.GetDisplayName()}(&{name}){TypeUtil.GetArrayDimension(cppArrayType2)}";
        }
        else if (cppType is CppArrayType cppArrayType3)
        {
            var lowType = TypeUtil.GetArrayElementType(cppArrayType3);
            return $"{lowType.GetDisplayName()}({name}){TypeUtil.GetArrayDimension(cppArrayType3)}";
        }

        return $"{cppType.GetDisplayName()} {name}";
    }

    public static CppType ReplaceFirstRefToPointer(CppType inType)
    {
        var cppType = inType.Clone();
        if (cppType is CppReferenceType cppReferenceType)
        {
            return new CppPointerType(cppReferenceType.ElementType);
        }

        if (cppType is CppTypeWithElementType elementType)
        {
            var subReplaced = ReplaceFirstRefToPointer(elementType);
            elementType.ElementType = subReplaced;
            return cppType;
        }

        return cppType;
    }

    public static string GetCppSimpleName(CppType cppType)
    {
        var name = cppType.FullName;
        name = name.Replace("::", "_");
        name = name.Replace("<", "_");
        name = name.Replace(">", "");
        name = name.Replace(",", "_");
        name = name.Replace(" ", "");
        name = name.Replace("*", "Ptr");
        name = name.Replace("&", "Ref");
        name = name.Replace("-", "");
        return FirstToUpper(name);
    }
    
}